﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZCalculator.Core
{
    public sealed class ReverseNotationGenerator
    {
        private string _expression;
        private readonly Stack<Operator> _opStack;
        private readonly Queue<ExpressionNode> _expressionNodeQueue;

        public ReverseNotationGenerator(string exp)
        {
            if (string.IsNullOrWhiteSpace(exp))
                throw new ArgumentNullException("exp");

            _expression = exp;
            _opStack = new Stack<Operator>();
            _expressionNodeQueue = new Queue<ExpressionNode>();
        }

        public Operator Peek()
        {
            return _opStack.Peek();
        }

        public Operator Pop()
        {
            Operator op = _opStack.Pop();
            if (op is BinaryOperator)
            {
                _expressionNodeQueue.Enqueue(op);
            }
            return op;
        }

        public void Push(Operator op)
        {
            _opStack.Push(op);
        }

        public bool IsEmpty
        {
            get { return _opStack.Count == 0; }
        }

        public Queue<ExpressionNode> Generate()
        {
            int index = 0;
            StringBuilder numberBuilder = new StringBuilder();

            while (index <= _expression.Length - 1)
            {
                char c = _expression[index];
                if (c == ' ')
                {
                    index++;
                    continue;
                }

                if (Bracket.CharIsBracket(c))
                {
                    Bracket b = new Bracket(c);
                    if (!b.IsBeginning && numberBuilder.Length > 0)
                    {
                        _expressionNodeQueue.Enqueue(new Number(double.Parse(numberBuilder.ToString())));
                        numberBuilder.Clear();
                    }

                    if (!b.IsBeginning)
                    {
                        while (!IsEmpty && Peek().Priority <= b.Priority)
                        {
                            Pop();
                        }
                    }
                    Push(b);
                }
                else if (BinaryOperator.CharIsBinaryOperator(c))
                {
                    if (numberBuilder.Length > 0)
                    {
                        _expressionNodeQueue.Enqueue(new Number(double.Parse(numberBuilder.ToString())));
                        numberBuilder.Clear();
                    }

                    BinaryOperator newOp = new BinaryOperator(c);

                    while (!IsEmpty && newOp.Priority <= Peek().Priority && !((Peek()) is Bracket))
                    {
                        Pop();
                    }

                    Push(newOp);
                }
                else if (Char.IsDigit(c) || c == '.')
                {
                    numberBuilder.Append(c);
                }
                else
                {
                    throw new InvalidOperationException(string.Format("invalid character in expression: {0}", c));
                }

                index++;
            }

            if (numberBuilder.Length > 0)
            {
                _expressionNodeQueue.Enqueue(new Number(double.Parse(numberBuilder.ToString())));
            }

            while (_opStack.Count > 0)
            {
                _expressionNodeQueue.Enqueue(_opStack.Pop());
            }

            return _expressionNodeQueue;
        }
    }
    public abstract class ExpressionNode
    {
        public static ExpressionNode Parse(string expression)
        {
            if (string.IsNullOrWhiteSpace(expression))
                throw new ArgumentNullException("expression");

            Queue<ExpressionNode> expressionNodeQueue = GetExpressionNodeQueueFromExpressionStr(expression);
            return GetRootExpressionNodeFromExpressionNodeQueue(expressionNodeQueue);
        }

        private static ExpressionNode GetRootExpressionNodeFromExpressionNodeQueue(Queue<ExpressionNode> expressionNodeQueue)
        {
            Stack<ExpressionNode> evaluateStack = new Stack<ExpressionNode>();

            while (expressionNodeQueue.Count > 0)
            {
                ExpressionNode node = expressionNodeQueue.Dequeue();
                if (node is BinaryOperator)
                {
                    ((BinaryOperator)node).RNode = evaluateStack.Pop();
                    ((BinaryOperator)node).LNode = evaluateStack.Pop();
                    evaluateStack.Push(node);
                }
                else if (node is Number)
                {
                    evaluateStack.Push(node);
                }
            }

            return evaluateStack.Pop();
        }

        private static Queue<ExpressionNode> GetExpressionNodeQueueFromExpressionStr(string expression)
        {
            return new ReverseNotationGenerator(expression).Generate();
        }


        public abstract double Evaluate();
    }
}
